home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PCMania 73
/
PCMania CD73_1.iso
/
sharewar
/
utiles
/
viff
/
viffedit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-02-18
|
29KB
|
1,200 lines
/************************* -*- Mode: C -*- *****************************
*
* viffedit.c -- emacs commands for viff edit mode
*
* Copyright (C) 1995-1998 Richard Flamsholt S0rensen. All rights reserved.
*
* Author : Richard Flamsholt S0rensen
* Created On : Tue Oct 24 22:57:36 1995
* Last Modified By: Richard Flamsholt S0rensen
* Last Modified On: Wed Feb 18 14:15:40 1998
* Update Count : 200
* Revision History: None
*
* COMMENTS
* HISTORY
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include "viff.h"
#define LINETABNLINC 20
#define ROOMINC 30
#define KILLINC 200
#define MAX_SEARCH_LEN 50
#define SEARCH_RESULT_CACHE 30
typedef struct {
char *string;
char flags;
int len;
long match;
} SEARCH_RESULT;
static char *editmode_help[] = {
" Viff editor",
"",
"kill to end: C-k",
"open up a line: C-o",
"search forward: C-s",
"search backward: C-r",
"insert killed text: C-y",
"goto start of text: C-x <",
"goto end of text: C-x >",
"goto a line: C-x C-l",
"zap trailing space: C-x C-z",
"",
"exit to local diff: C-l",
"exit editmode: esc ins",
""
};
static char *searchmode_help[] = {
" Viff search",
"",
"forward search: C-s case sensitivity: C-c",
"backward search: C-r match whole word: C-w",
"",
"stop search and go back to current diff: C-g",
"stop search and go to local diff: C-l",
"stop search and enter editmode: esc ins",
"",
"toggle between text and search string: tab",
"",
"any other key:",
" in text; exit search and enter editmode",
" in search; edit the search string",
""
};
static char *gotoline_help[] = {
"To goto a specific line you need",
"to tell whether you wish to go to",
"a line in file1 or file2.",
"",
" goto a line in file1: 1",
" goto a line in file2: 2"
};
static int Memx, Winx, Winy;
static int StickyWinx;
static LINE *Winl;
static char *kill_text;
static int kill_len;
static int kill_room;
static BOOLEAN kill_append;
static BOOLEAN editmode = FALSE;
static void cleanup_edit_mode(void);
static void ctrl_x_command(void);
static void zap_trailing_whitespace(void);
static BOOLEAN search_forward(int line, int pos, char *str, int len,
BOOLEAN fold, BOOLEAN wholeword);
static BOOLEAN search_backward(int line, int pos, char *str, int len,
BOOLEAN fold, BOOLEAN wholeword);
static void edit_goto_line(void);
static void edit_goto_pos(int line, int memx);
static void edit_focus_line(int line);
static void set_Winl(void);
static void set_Memx(int memx);
static void set_valid_Memx(void);
static void set_Scrollx(void);
static void enlarge_line(LINE *line, int len);
static void update_killed_text(char *add, int len);
static void edit_top(void);
static void edit_bottom(void);
static void edit_home(void);
static void edit_end(void);
static BOOLEAN edit_delete(void);
static BOOLEAN edit_up(void);
static BOOLEAN edit_down(void);
static BOOLEAN edit_left(void);
static BOOLEAN edit_right(void);
static BOOLEAN edit_insert_ch(int ch);
static BOOLEAN edit_insert_str(char *text);
void
init_edit_mode(void)
{
kill_text = NULL;
atexit(cleanup_edit_mode);
}
static void
cleanup_edit_mode(void)
{
free(kill_text);
}
void
edit_mode(void)
{
int ch;
BOOLEAN append = FALSE;
status_attr = edit_attr;
if (!editmode) {
editmode = TRUE;
getyx(stdscr, Winy, Winx/*unused*/);
StickyWinx = Winx = Memx = 0;
Scrollx = 0;
set_Winl();
if (Winy < VIEWLINES && topln+Winy == diffmid) { /* now viewing curr diff */
if (linetbl[diffbeg].incl) {
edit_up();
} else {
edit_down();
}
}
if (Winy < SCROLLSIGHT) {
Winy = MIN(nline-1, SCROLLSIGHT);
set_Winl();
} else if (VIEWLINES-SCROLLSIGHT < Winy) {
Winy = VIEWLINES-SCROLLSIGHT;
set_Winl();
}
}
for (;;) {
edit_statusline(Winl, Memx, Winx);
(void)move(Winy, Winx-Scrollx);
(void)refresh();
ch = getch();
kill_append = append;
append = FALSE;
switch (ch) {
case KEY_ESCAPE:
case KEY_IC:
case KEY_EIC:
status_attr = viff_attr;
edit_home();
goto_curr_diff(FALSE);
editmode = FALSE;
return;
case KEYCTRL('L'):
status_attr = viff_attr;
edit_home();
goto_nearest_diff(topln+Winy);
editmode = FALSE;
return;
#ifdef KEY_SHELP
case KEY_SHELP:
#endif
#ifdef KEY_LHELP
case KEY_LHELP:
#endif
case KEY_F(1):
help(editmode_help, ARRAYSIZE(editmode_help));
break;
case KEY_BACKSPACE:
case '\x08':
/* Allow deleting an empty line by using backspace even if the
* line above is a system line. In that special case we delete
* the line using delete() and move up onto the line above.
*/
if (Winy > 0 && SYSLINE(Winl-1) && !SYSLINE(Winl) && Winl->len == 0) {
if (edit_delete()) edit_up();
} else {
if (edit_left()) edit_delete();
}
break;
case KEY_DELETE:
case KEY_DC:
case KEYCTRL('D'): edit_delete(); break;
case KEY_UP:
case KEYCTRL('P'): edit_up(); break;
case KEY_DOWN:
case KEYCTRL('N'): edit_down(); break;
case KEY_LEFT:
case KEYCTRL('B'): edit_left(); break;
case KEY_RIGHT:
case KEYCTRL('F'): edit_right(); break;
case KEY_HOME:
case KEYCTRL('A'): edit_home(); break;
case KEY_END:
case KEYCTRL('E'): edit_end(); break;
case KEYCTRL('Y'): edit_insert_str(kill_text); break;
case KEYCTRL('O'):
{ int scrollx = Scrollx;
if (edit_insert_ch('\n')) {
edit_left();
Scrollx = scrollx;
putline(topln+Winy);
move(Winy, Winx-Scrollx);
}
}
break;
#ifdef KEY_EOL
case KEY_EOL:
#endif
case KEYCTRL('K'):
if (SYSLINE(Winl)) break;
if (Memx == Winl->len) { /* at end of line */
update_killed_text("\n", 1);
edit_delete();
} else {
update_killed_text(Winl->txt+Memx, Winl->len-Memx);
Winl->len = Memx;
putline(topln+Winy);
}
append = TRUE;
break;
case KEYCTRL('S'):
case KEYCTRL('R'):
search(ch);
break;
case KEY_PPAGE: goto_prev_page(); break;
case KEY_NPAGE:
case KEYCTRL('V'): goto_next_page(); break;
case KEYCTRL('X'):
ctrl_x_command();
break;
default:
edit_insert_ch(ch);
break;
}
}
}
static void
ctrl_x_command(void)
{
int ch;
statusline(0, "C-x");
(void)move(VIEWLINES, 5);
(void)refresh();
ch = getch();
if (KEYABORT(ch)) return;
switch (ch) {
case '<':
edit_top();
break;
case '>':
edit_bottom();
break;
case KEYCTRL('L'):
edit_goto_line();
break;
case KEYCTRL('Z'):
zap_trailing_whitespace();
break;
default:
(void)beep();
}
}
static void
zap_trailing_whitespace(void)
{
LINE *winl;
unsigned long zap;
int i;
zap = 0;
for (i=0, winl=linetbl; i < nline; i++, winl++) {
if (SYSLINE(winl)) continue;
while (winl->len > 0 && isspace(((unsigned char*)winl->txt)[winl->len-1])) {
winl->len--;
zap++;
}
}
set_valid_Memx();
for (i = 0; i < VIEWLINES; i++) {
putline(topln+i);
}
if (zap > 0) modified = TRUE;
message("%lu space%s zapped", zap, zap==1?"":"s");
}
/**********************************************************************/
/* To speed up the search as much as possible I have taken out the
foldcase/wholeword test from the inner loops and made four tight
loops for each foldcase/wholeword combination instead. This macro
embodies the code and run 20-50% faster.
*/
#define SEARCH_DO(wwif1,wwif2,foldfunc,inputbuffer) \
{ int i, j, count; \
LINE *winl; \
char *p; \
for (i=0, winl=linetbl; i < nline; i++, winl++) { \
if (SYSLINE(winl)) continue; \
count = winl->len-len; \
for (j=0, p=winl->txt; j <= count; j++, p++) { \
wwif1 \
{ int n = len; \
char *s1 = inputbuffer; \
char *s2 = p; \
while (*s1++ == foldfunc(*s2++)) \
if (--n == 0) { \
wwif2 match++; \
break; \
} \
} \
} \
} \
}
#define WWIF1 if (j==0 || (!isalnum(p[-1]) && p[-1]!='_'))
#define WWIF2 if (j==count || !isalnum(*s2) && *s2!='_')
void
search(int search_key)
{
static BOOLEAN foldcase = TRUE;
static BOOLEAN wholeword = FALSE;
static char lastsearch[MAX_SEARCH_LEN+1];
SEARCH_RESULT stbl[SEARCH_RESULT_CACHE];
int i, sfirst, slast;
char buf[MAX_SEARCH_LEN];
char foldbuf[MAX_SEARCH_LEN];
char matchbuf[30]; /* to sprintf("%ld") into */
BOOLEAN in_text;
BOOLEAN has_searched;
int oldch, ch;
int firstpos, pos, len;
long match;
int memx, line;
if (!editmode) {
(void)getyx(stdscr, Winy, Winx/*unused*/);
StickyWinx = Winx = Memx = 0;
Scrollx = 0;
set_Winl();
}
in_text = FALSE;
has_searched = FALSE;
pos = len = 0;
sfirst = slast = 0;
memx = Memx;
line = topln+Winy;
ch = '\0';
for (;;) {
match = 0;
if (len > 0) {
char flags = 0;
if (foldcase) flags |= 0x01;
if (wholeword) flags |= 0x02;
for (i = 0; i < len; i++) {
foldbuf[i] = (char)tolower(((unsigned char*)buf)[i]);
}
for (i = sfirst; i != slast; i = (i+1)%SEARCH_RESULT_CACHE) {
if (stbl[i].flags == flags &&
stbl[i].len == len &&
memcmp(stbl[i].string, buf, len) == 0) {
match = stbl[i].match;
break;
}
}
if (i == slast) {
if (foldcase) {
if (wholeword) {
SEARCH_DO(WWIF1,WWIF2,(tolower),foldbuf);
} else {
SEARCH_DO(;,;,(tolower),foldbuf);
}
} else {
if (wholeword) {
SEARCH_DO(WWIF1,WWIF2,0+,buf);
} else {
SEARCH_DO(;,;,0+,buf);
}
}
stbl[slast].flags = flags;
stbl[slast].string = memcpy(xmalloc(len), buf, len);
stbl[slast].len = len;
stbl[slast].match = match;
slast = (slast+1) % SEARCH_RESULT_CACHE;
if (slast == sfirst) {
free(stbl[sfirst].string);
sfirst = (sfirst+1) % SEARCH_RESULT_CACHE;
}
}
lastsearch[0] = '\0';
}
sprintf(matchbuf, "%ld", match);
statusline(STATUS_HELP,
"%c%cearch%c%*s[%s]:%n%-*.*s",
wholeword ? '\"' : ' ',
foldcase ? 's' : 'S',
wholeword ? '\"' : ' ',
len==0? 4 :match<10? 3 :match<100? 2 :match<1000? 1 : 0, "",
len>0 ? matchbuf : "",
&firstpos, MAX_SEARCH_LEN, len, buf);
for (;;) {
if (in_text) {
(void)move(Winy, Winx-Scrollx);
} else {
(void)move(VIEWLINES, 1+firstpos+pos);
}
(void)refresh();
oldch = ch;
ch = getch();
if (KEYABORT(ch)) goto search_done;
if (KEYRETURN(ch)) ch = search_key;
switch (ch) {
case KEYCTRL('L'):
case KEY_IC:
goto search_done;
case KEYCTRL('C'):
foldcase = !foldcase;
goto search_again;
case KEYCTRL('W'):
wholeword = !wholeword;
goto search_again;
#ifdef KEY_SHELP
case KEY_SHELP:
#endif
#ifdef KEY_LHELP
case KEY_LHELP:
#endif
case KEY_F(1):
help(searchmode_help, ARRAYSIZE(searchmode_help));
break;
case KEYCTRL('S'):
case KEYCTRL('R'):
search_key = ch;
if (lastsearch[0]) {
len = pos = strlen(lastsearch);
memcpy(buf, lastsearch, len);
} else if (len > 0) {
BOOLEAN found;
char *s = foldcase ? foldbuf : buf;
int x = Memx;
int y = topln+Winy;
if (ch == KEYCTRL('R')) {
static BOOLEAN found_r=FALSE;
if (!found_r && oldch == ch) {
x = 0;
y = nline;
}
found=found_r= search_backward(y, x, s, len, foldcase, wholeword);
} else {
static BOOLEAN found_s=FALSE;
if (!found_s && oldch == ch) {
x = 0;
y = 0;
}
found=found_s= search_forward(y, x, s, len, foldcase, wholeword);
}
if (found) {
in_text = TRUE;
has_searched = TRUE;
} else {
(void)beep();
}
}
goto search_again;
case '\t':
in_text = !in_text;
goto search_again;
case KEY_BACKSPACE:
case '\x08':
if (in_text) goto search_done;
if (pos > 0) {
memmove(buf+pos-1, buf+pos, len-pos);
pos--; len--;
goto search_again;
}
break;
case KEY_DELETE:
case KEY_DC:
case KEYCTRL('D'):
if (in_text) goto search_done;
if (pos < len) {
memmove(buf+pos, buf+pos+1, len-pos-1);
len--;
goto search_again;
}
break;
case KEY_LEFT:
case KEYCTRL('B'):
if (in_text) goto search_done;
if (pos > 0) pos--;
break;
case KEY_RIGHT:
case KEYCTRL('F'):
if (in_text) goto search_done;
if (pos < len) pos++;
break;
case KEY_HOME:
case KEYCTRL('A'):
if (in_text) goto search_done;
pos = 0;
break;
case KEY_END:
case KEYCTRL('E'):
if (in_text) goto search_done;
pos = len;
break;
#ifdef KEY_EOL
case KEY_EOL:
#endif
case KEYCTRL('K'):
if (in_text) goto search_done;
if (pos < len) {
len = pos;
goto search_again;
}
break;
default:
if (in_text) goto search_done;
if (PRINTABLE_CHAR(ch) && len < sizeof(buf)) {
memmove(buf+pos+1, buf+pos, len-pos);
buf[pos] = (char)ch;
pos++; len++;
goto search_again;
}
}
} search_again:;
} search_done:;
while (sfirst != slast) {
free(stbl[sfirst].string);
sfirst = (sfirst+1) % SEARCH_RESULT_CACHE;
}
memcpy(lastsearch, buf, len);
if (ch == KEYCTRL('G')) {
if (editmode) {
edit_goto_pos(line, memx);
} else {
edit_home();
goto_curr_diff(FALSE);
}
} else if (ch == KEYCTRL('L')) {
goto_nearest_diff(topln+Winy);
} else {
if (ch == KEY_ESCAPE || ch == KEY_IC) {
if (!has_searched) return;
} else {
(void)ungetch(ch);
}
if (!editmode) {
editmode = TRUE; /* don't reposition the cursor */
edit_mode();
}
}
}
static BOOLEAN
search_forward(int line, int pos, char *str, int len,
BOOLEAN fold, BOOLEAN wholeword)
{
int i, j, count;
LINE *winl;
char *p;
for (i=line, winl=linetbl+i; i < nline; i++, winl++, pos=0) {
if (SYSLINE(winl)) continue;
count = winl->len-len;
for (j=pos, p=winl->txt+j; j <= count; j++, p++)
if (!wholeword || j==0 || (!isalnum(p[-1]) && p[-1]!='_')) {
int n = len;
char *s1 = str;
char *s2 = p;
while (*s1++ == (fold ? (tolower)(*s2++) : *s2++))
if (--n == 0)
if (!wholeword || j==count || !isalnum(*s2) && *s2!='_') {
edit_goto_pos(i, j+len);
return TRUE;
}
}
}
return FALSE;
}
static BOOLEAN
search_backward(int line, int pos, char *str, int len,
BOOLEAN fold, BOOLEAN wholeword)
{
int i, j, count;
LINE *winl;
char *p;
if (pos-- == 0) {
pos = INT_MAX;
if (line-- == 0) return FALSE;
}
for (i=line, winl=linetbl+i; i >= 0; i--, winl--, pos=INT_MAX) {
if (SYSLINE(winl)) continue;
count = winl->len-len;
if (count > pos) count = pos;
for (j=count, p=winl->txt+j; j >= 0; j--, p--)
if (!wholeword || j==0 || (!isalnum(p[-1]) && p[-1]!='_')) {
int n = len;
char *s1 = str;
char *s2 = p;
while (*s1++ == (fold ? (tolower)(*s2++) : *s2++))
if (--n == 0)
if (!wholeword || j==count || !isalnum(*s2) && *s2!='_') {
edit_goto_pos(i, j);
return TRUE;
}
}
}
return FALSE;
}
/**********************************************************************/
static void
set_Winl(void)
{
Winl = linetbl+topln+Winy;
}
static void
set_Memx(int memx)
{
for (Winx=0, Memx=0; Memx < memx; Memx++) {
if (Winl->txt[Memx] == '\t') {
Winx += tabwidth-Winx%tabwidth;
} else {
Winx++;
}
}
set_Scrollx();
}
static void
set_valid_Memx(void)
{
int winx;
char *p;
if (SYSLINE(Winl)) {
Memx = 0;
} else {
for (winx=0, Memx=0, p=Winl->txt; Memx < Winl->len; Memx++, p++) {
if (Winx <= winx) break;
if (*p == '\t') {
winx += tabwidth-winx%tabwidth;
} else {
winx++;
}
}
Winx = winx;
}
set_Scrollx();
}
static void
set_Scrollx(void)
{
int i;
int old_scrollx = Scrollx;
if (SYSLINE(Winl)) {
Winx = 0;
Scrollx = 0;
}
if (Winx >= Scrollx+COLS) { /* going too far to the right */
Scrollx = Winx-COLS/2;
} else if (Winx <= Scrollx) { /* going too far to the left */
if (0 < Scrollx) { /* window is scrolled; scroll back */
if (Winx < COLS/5*4) { /* we prefer not to scroll x at all */
Scrollx = 0;
} else { /* too far out; try centering x */
Scrollx = Winx-COLS/2;
}
}
}
if (Scrollx == old_scrollx) { /* no need to scroll window */
putline(topln+Winy);
} else { /* redraw entire window */
for (i = 0; i < VIEWLINES; i++) {
putline(topln+i);
}
}
move(Winy, Winx-Scrollx);
}
static void
update_killed_text(char *add, int len)
{
if (!kill_append) {
kill_len = 0;
}
if (kill_room < kill_len+len+1) {
kill_room = kill_len+len+1+KILLINC;
kill_text = xrealloc(kill_text, kill_room);
}
memcpy(kill_text+kill_len, add, len);
kill_len += len;
kill_text[kill_len] = '\0';
}
static void
enlarge_line(LINE *line, int len)
{
if (line->room < len) {
line->room = len + ROOMINC;
line->txt = xrealloc(line->txt, line->room);
}
}
void
goto_next_page(void)
{
int ln;
if (topln == nline-VIEWLINES) {
edit_bottom();
return;
}
ln = topln+PAGELINES;
if (nline-VIEWLINES < ln) ln = nline-VIEWLINES;
if (0 < ln) set_topline(ln);
if (Winy < SCROLLSIGHT-1) {
Winy = MIN(nline-1, SCROLLSIGHT-1);
}
set_Winl();
Winx = StickyWinx;
set_valid_Memx();
}
void
goto_prev_page(void)
{
int ln;
if (topln == 0) {
edit_top();
return;
}
ln = topln-PAGELINES;
if (ln < 0) ln = 0;
set_topline(ln);
if (VIEWLINES-SCROLLSIGHT < Winy) {
Winy = VIEWLINES-SCROLLSIGHT;
}
set_Winl();
Winx = StickyWinx;
set_valid_Memx();
}
static void
edit_top(void)
{
set_topline(0);
Winy = 0;
set_Winl();
edit_home();
}
static void
edit_bottom(void)
{
if (nline <= VIEWLINES) {
Winy = nline-1;
} else {
set_topline(nline-VIEWLINES);
Winy = VIEWLINES-1;
}
set_Winl();
edit_end();
}
static void
edit_home(void)
{
Winx = StickyWinx = 0;
set_valid_Memx();
}
static void
edit_end(void)
{
if (SYSLINE(Winl)) {
Winx = 0;
} else {
Winx = Winl->len*tabwidth;
}
set_valid_Memx();
StickyWinx = Winx;
}
static void
edit_goto_line(void)
{
int ch;
int i, last;
char buf[200];
int lineno, ln;
LINE *line;
ch = ask(gotoline_help, ARRAYSIZE(gotoline_help),
"12", KEY_ESCAPE, "goto line in file1 or file2?");
if (ch == KEY_ESCAPE) return;
if (!input(INPUT_NUMERIC, buf, "goto line:")) return;
lineno = atoi(buf);
if (lineno == 0) lineno = 1;
last = -1;
for (i=0, line=linetbl; i < nline; i++, line++) {
if (line->type == COMMON || line->type == DIFF) {
last = i;
ln = ch=='1' ? line->line1 : line->line2;
if (ln > lineno) break;
if (ln == lineno) {
edit_focus_line(i);
return;
}
}
}
if (last == -1) { /* highly improbable */
message("there are no lines to go to");
} else {
edit_focus_line(last);
message("there is no line %s; going to line %d instead", buf, ln);
}
}
static void
edit_goto_pos(int line, int memx)
{
if (topln+SCROLLSIGHT <= line && line < topln+VIEWLINES-SCROLLSIGHT) {
Winy = line-topln;
set_Winl();
} else {
edit_focus_line(line);
}
set_Memx(memx);
StickyWinx = Winx;
}
static void
edit_focus_line(int line)
{
int ln;
ln = line - LINES/2;
if (nline-VIEWLINES < ln) ln = nline-VIEWLINES;
if (ln < 0) ln = 0;
set_topline(ln);
Winy = line - topln;
set_Winl();
edit_home();
}
static BOOLEAN
edit_insert_ch(int ch)
{
if (KEYRETURN(ch)) return edit_insert_str("\n");
if (SYSLINE(Winl)) return FALSE;
if (PRINTABLE_CHAR(ch)) {
char buf[2];
buf[0] = (char)ch; buf[1] = '\0';
return edit_insert_str(buf);
}
return FALSE;
}
static BOOLEAN
edit_insert_str(char *text)
{
LINE *winl;
int memx, newl;
char *p, *pend;
int i;
if (!text || !*text) return FALSE; /* maybe say "Nothing to yank" */
for (newl=0, p=text; *p; p++) {
if (*p == '\n') newl++;
}
if (SYSLINE(Winl)) {
if (text<p && p[-1] != '\n') { /* must have a terminating \n */
if (!edit_insert_str("\n") || !edit_up()) return FALSE;
} else {
Winl->room = 0;
Winl->len = 0;
Winl->txt = NULL;
}
}
if (newl == 0) { /* text contains no newlines */
int len = (int)(p-text);
enlarge_line(Winl, Winl->len+len);
memmove(Winl->txt+Memx+len, Winl->txt+Memx, Winl->len-Memx);
memmove(Winl->txt+Memx, text, len);
Winl->len += len;
memx = Memx+len;
} else {
BOOLEAN incl;
LINETYPE type;
int pushlen, inslen;
int ln = topln+Winy;
int winy;
if (ln <= diffbeg) diffbeg += newl;
if (ln <= diffmid) diffmid += newl;
if (ln <= diffend) diffend += newl;
if (linetblsiz <= nline+newl) {
linetblsiz += newl+LINETABNLINC;
linetbl = xrealloc(linetbl, sizeof(*linetbl)*linetblsiz);
set_Winl(); /* adjust Winl */
}
memmove(Winl+newl+1, Winl+1, (nline-ln-1)*sizeof(*Winl));
nline += newl;
/* For type and inclusion of new inserted lines we look at the current
* line (0), the one above (-1), or use a fixed value:
*
* incl type when inserting at a line like
* 0 0 ...
* TRUE COMMON vvvvvv
* 0 0 ...
* -1 DIFF ------
* 0 0 ...
* 0 DIFF ^^^^^^
* 0 0 ...
*/
switch (Winl->type) {
case TOPSEP: incl = TRUE; type = COMMON; break;
case MIDSEP: incl = Winl[-1].incl; type = DIFF; break;
case BOTSEP: incl = Winl[ 0].incl; type = DIFF; break;
default: incl = Winl[ 0].incl; type = Winl[0].type; break;
}
Winl[newl].incl = Winl->incl;
Winl[newl].type = Winl->type;
for (i=0, winl=Winl; i < newl; i++, winl++) {
winl->incl = incl;
winl->type = type;
}
Winl[newl].line1 = Winl->line1;
Winl[newl].line2 = Winl->line2;
Winl[newl].new = Winl->new;
Winl[newl].broken = Winl->broken;
for (i=1, winl=Winl+i; i < newl; i++, winl++) {
winl->new = TRUE;
}
if (SYSLINE(Winl) || Memx == 0) {
Winl->new = TRUE;
Winl->broken = FALSE;
} else if (Memx == Winl->len) {
winl->new = TRUE;
winl->broken = FALSE;
} else { /* line is broken; mark that */
winl->line1 = Winl->line1;
winl->line2 = Winl->line2;
Winl->new = winl->new = FALSE;
Winl->broken = winl->broken = TRUE;
}
/* curr text: insert between B and C:
* AxxxxxBCxxxxxD\0 1xxx2\n
* ExxxF\0 3xxxxxx4
*
* pushlen= len from C to D
* inslen = len from 1 to 2 (excluding \n)
*/
winl = Winl; memx = Memx;
p = strchr(text, '\n'); /* we know there's a newline */
inslen = (int)(p-text);
pushlen = winl->len - memx;
do {
p++;
Winl++;
for (pend = p; *pend; pend++) {
if (*pend == '\n') break;
}
Memx = (int)(pend-p);
if (!SYSLINE(Winl)) {
Winl->room = 0;
Winl->len = Memx;
Winl->txt = memcpy(xmalloc(Winl->len), p, Winl->len);
}
p = pend;
} while (*p == '\n');
if (!SYSLINE(Winl)) {
Winl->len = Memx + pushlen;
enlarge_line(Winl, Winl->len);
memmove(Winl->txt+Memx, winl->txt+memx, pushlen);
}
winl->len = memx + inslen;
enlarge_line(winl, winl->len);
memmove(winl->txt+memx, text, inslen);
memx = Memx;
Memx = 0; Winx = 0;
Winy += newl; /* add first, ask questions later: */
if (PAGELINES < Winy) {
int new_topln = topln + Winy - PAGELINES;
if (nline-VIEWLINES < new_topln) {
new_topln = MAX(0, nline-VIEWLINES);
}
Winy -= new_topln-topln;
set_topline(new_topln);
}
set_Winl();
winy = MAX(0, Winy-newl);
move(winy, 0);
for (i = winy; i < Winy; i++) (void)insertln();
for (i = winy; i < Winy; i++) putline(topln+i);
}
putline(topln+Winy);
set_Memx(memx);
StickyWinx = Winx;
modified = TRUE;
return TRUE;
}
static BOOLEAN
edit_delete(void)
{
int ln = topln+Winy;
if (SYSLINE(Winl)) return FALSE;
if (Memx < Winl->len) { /* not standing on the edge */
memmove(Winl->txt+Memx, Winl->txt+Memx+1, --Winl->len - Memx);
putline(topln+Winy);
} else { /* del newline */
if (ln == nline-1) return FALSE; /* this is the last line */
if (SYSLINE(Winl+1)) {
if (Winl->len > 0) return FALSE; /* can't collapse text+sys line */
free(Winl[0].txt);
Winl[0] = Winl[1];
} else { /* collapse with line below */
if (Winl[0].new || Winl[0].len == 0) { /* better to use lower line's */
Winl[0].line1 = Winl[1].line1;
Winl[0].line2 = Winl[1].line2;
Winl[0].new = Winl[1].new;
Winl[0].broken = Winl[1].broken;
}
Winl->len += Winl[1].len; /* this line will now hold both */
enlarge_line(Winl, Winl->len); /* add some extra space now */
memcpy(Winl->txt+Memx, Winl[1].txt, Winl[1].len);
free(Winl[1].txt);
}
memmove(Winl+1, Winl+2, (nline-ln-2)*sizeof(*Winl));
if (ln < diffbeg) diffbeg--;
if (ln < diffmid) diffmid--;
if (ln < diffend) diffend--;
(void)deleteln();
if (nline-- == topln+VIEWLINES && 0 < topln) { /* pull text above down */
set_topline(topln-1);
Winy++;
set_Winl();
} else {
putline(topln+VIEWLINES-1); /* bottom line is empty; redraw it */
}
putline(ln); /* redraw this new, collapsed line */
}
move(Winy, Winx-Scrollx); /* (x,y) isn't changed, so go back */
StickyWinx = Winx;
modified = TRUE;
return TRUE;
}
static BOOLEAN
edit_up(void)
{
if (Winy == 0) return FALSE;
if (Winy < SCROLLSIGHT && 0 < topln) {
set_topline(topln-1);
} else {
Winy--;
}
set_Winl();
Winx = StickyWinx;
set_valid_Memx();
return TRUE;
}
static BOOLEAN
edit_down(void)
{
if (VIEWLINES-1 == Winy || nline-1 == Winy) return FALSE;
if (PAGELINES <= Winy && topln+VIEWLINES < nline) {
set_topline(topln+1);
} else {
Winy++;
}
set_Winl();
Winx = StickyWinx;
set_valid_Memx();
return TRUE;
}
static BOOLEAN
edit_left(void)
{
if (Winx == 0) {
if (!edit_up()) return FALSE;
edit_end();
return TRUE;
}
Winx--;
if (Winl->txt[Memx-1] == '\t') {
int winx, prevwinx;
char *p;
for (winx=prevwinx=0, p=Winl->txt; winx <= Winx; p++) {
prevwinx = winx;
if (*p == '\t') {
winx += tabwidth-winx%tabwidth;
} else {
winx++;
}
}
Winx = prevwinx;
}
set_valid_Memx();
StickyWinx = Winx;
return TRUE;
}
static BOOLEAN
edit_right(void)
{
if (!SYSLINE(Winl) && Memx < Winl->len) {
Winx++;
} else {
if (!edit_down()) return FALSE;
edit_home();
}
set_valid_Memx();
StickyWinx = Winx;
return TRUE;
}